package com.ete.service.service.location.serial

import android.serialport.SerialPort
import com.blankj.utilcode.util.ConvertUtils
import com.blankj.utilcode.util.Utils
import com.ete.service.entry.location.LocationEntry
import com.ete.service.entry.location.LocationResult
import com.ete.service.manager.ManagerConstant
import com.ete.service.manager.location.LocationManager
import com.ete.service.service.R
import com.ete.service.service.location.ILocationListener
import com.ete.service.util.log.EteLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset

/**
 * Author:LiXian
 * <p>
 * Desc:串口GPS定位
 * Version:1.0
 * date 2025/4/30 9:27
 */
class SerialLocation : ILocationListener() {

    companion object {
        private const val PRINT_DELAY = 5 * 60 * 1000L
    }

    private var mIntervalTime: Long = 0L
    private var mCallbackTime: Long = 0L
    private var mInputStream: InputStream? = null
    private var mOutputStream: OutputStream? = null

    private var isLocation: Boolean = false
    private var mLocationJob: Job? = null
    private var mReadLocationTime: Long = 0L

    private var mPrintTime: Long = 0L
    private var mParsingTime: Long = 0L

    private var mLocationEntry: LocationEntry? = null
    private var mLocationResult: LocationResult? = null

    override fun initLocation(mEntry: LocationEntry) {
        this.mLocationEntry = mEntry
        mIntervalTime = mEntry.interval * 1000L
        mCallbackTime = mEntry.callbackInterval * 1000L
        try {
            val mSerialPort: SerialPort? = SerialPort.newBuilder(ManagerConstant.GPS_LOCATION_SERIAL_PATH,
                    ManagerConstant.GPS_LOCATION_BAUD_RATE).build()
            mInputStream = mSerialPort?.inputStream
            mOutputStream = mSerialPort?.outputStream
        } catch (ex: Exception) {
            EteLog.d(LocationManager.TAG_SERIAL, "初始化GPS串口定位失败error:[$ex]")
        }
    }

    override fun startLocation(): Int {
        EteLog.d(LocationManager.TAG_SERIAL, "开始GPS串口定位")
        if (mInputStream == null) {
            initLocation(LocationEntry())
        }
        if (mInputStream == null) {
            EteLog.d(LocationManager.TAG_SERIAL, "此设备无GPS模块")
            return ManagerConstant.CODE_NOT_GPS_MODE
        }
        writeCommand()
        cancelLocationJob()
        isLocation = true
        mLocationJob = CoroutineScope(Dispatchers.IO).launch {
            while (isLocation) {
                try {
                    val data = ByteArray(512)
                    val length = mInputStream?.read(data) ?: -1
                    if (length > 0) {
                        val newData = data.copyOf(length)
                        parsingData(newData)
                        mReadLocationTime = System.currentTimeMillis()
                    }
                } catch (ex: Exception) {
                    destroyLocation()
                }
            }
        }
        return ManagerConstant.CODE_SUCCESS
    }

    override fun checkedPermission(mCallback: (Int) -> Unit) {
        if (mInputStream == null) initLocation(LocationEntry())
        if (mInputStream == null) {
            mCallback.invoke(ManagerConstant.CODE_NOT_GPS_MODE)
        } else {
            mCallback.invoke(ManagerConstant.CODE_SUCCESS)
        }
    }

    override fun getLocationResult(): LocationResult? {
        return mLocationResult
    }

    override fun stopLocation() {
        super.stopLocation()
        cancelLocationJob()
    }

    override fun destroyLocation() {
        super.destroyLocation()
        stopLocation()
        try {
            mInputStream?.close()
            mLocationEntry?.mCallback = null
            mLocationEntry = null
            mLocationResult = null
        } catch (ex: Exception) {
            ex.toString()
        } finally {
            mInputStream = null
        }
    }

    override fun isSerialLocation(): Boolean {
        return true
    }

    override fun isLocationning(): Boolean {
        return isLocation
    }

    override fun isGpsModel(): Boolean {
        return System.currentTimeMillis() - mReadLocationTime < 2000L
    }

    private fun cancelLocationJob() {
        isLocation = false
        mLocationJob?.cancel()
        mLocationJob = null
    }

    /**
     * 解析数据
     *
     * 注：数据格式：$GNGLL,2235.20782,N,11351.55837,E,090732.000,A,A*4A
     */
    private suspend fun parsingData(datas: ByteArray) {
        val isParsing = System.currentTimeMillis() - mParsingTime > mIntervalTime
                || mLocationResult == null
        if (!isParsing) return
        mParsingTime = System.currentTimeMillis()
        try {
            val mDataHex = String(datas, Charset.forName("GBK"))
            val splits = mDataHex.split(delimiters = arrayOf(","))
            val latTemp = splits.getOrNull(1) ?: ""
            val lonTemp = splits.getOrNull(3)?.split(delimiters = arrayOf(".")) ?: listOf()
            var lastLatitude: Double = ManagerConstant.GPS_DEFAULT_ADDRESS
            var lastLongitude: Double = ManagerConstant.GPS_DEFAULT_ADDRESS
            if (latTemp.isNotEmpty() && lonTemp.isNotEmpty() && lonTemp.size == 2) {
                //纬度整数部分，latTemp前2位为纬度整数部分
                val latInteger = latTemp.substring(0, 2).toIntOrNull() ?: return
                //纬度小数部分，latTemp之后的部分除以60，为纬度小数部分
                val latDecimalTmp = latTemp.substring(2, latTemp.length).toDoubleOrNull() ?: return
                val latDecimal = latDecimalTmp / 60.0
                //最后的纬度
                val latitude = latInteger + latDecimal

                val lonTemp1 = lonTemp[0]
                //经度整数部分，lonTemp第一个字段，前2位或3位，为纬度整数部分
                val lngInteger = (if (lonTemp1.length == 5) lonTemp1.substring(0, 3) else lonTemp1.substring(0, 2)).toIntOrNull()
                        ?: return
                //经度小数部分，lonTemp第二个字段，
                val lngDecimalTmp = (lonTemp1.substring(lngInteger.toString().length, lonTemp1.length) + "." + lonTemp[1]).toDoubleOrNull()
                        ?: return
                val lngDecimal = lngDecimalTmp / 60.0
                //最后的经度
                val longitude = lngInteger + lngDecimal

                //经纬度保留7位小数
                lastLatitude = String.format("%.7f", latitude).toDoubleOrNull() ?: return
                lastLongitude = String.format("%.7f", longitude).toDoubleOrNull() ?: return
                if (lastLatitude == lastLongitude) return

                mLocationResult = LocationResult(
                        latitude = lastLatitude,
                        longitude = lastLongitude,
                        time = System.currentTimeMillis(),
                        code = ManagerConstant.CODE_SUCCESS,
                        result = Utils.getApp().getString(R.string.location_success)
                )
                withContext(Dispatchers.Main) {
                    mLocationEntry?.mCallback?.invoke(mLocationResult!!)
                }
            }

            val isPrint = System.currentTimeMillis() - mPrintTime > PRINT_DELAY
            if (isPrint) {
                EteLog.d(LocationManager.TAG_SERIAL, "收到定位数据mDataHex:[$mDataHex]" +
                        ",纬度lat:[$lastLatitude]" +
                        ",经度lat:[$lastLongitude]"
                )
                mPrintTime = System.currentTimeMillis()
            }
        } catch (ex: Exception) {
            EteLog.d(LocationManager.TAG_SERIAL, "解析数据错误error:[${ex}]")
        }
    }

    private fun writeCommand() {
        try {
            val command = "24 50 43 41 53 30 33 2C 30 2C 31 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2C 30 2A 30 33 0D 0A"
                .replace(" ", "").trim()
            val mBytes = ConvertUtils.hexString2Bytes(command)
            mOutputStream?.write(mBytes)
            EteLog.d(LocationManager.TAG_SERIAL, "写入命令command:[${ConvertUtils.bytes2HexString(mBytes)}]")
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
    }
}